package fs

import (
	"context"
	"fmt"
	"os"
	"strings"
	"syscall"

	"gitee.com/rdor/pod-filebrowser/pkg/k8s"
	"github.com/hanwen/go-fuse/v2/fs"
	"github.com/hanwen/go-fuse/v2/fuse"
	corev1 "k8s.io/api/core/v1"
)

const mountPath = "/srv"

// K8sFS represents a FUSE filesystem that maps to a container's filesystem
type K8sFS struct {
	client      *k8s.Client
	root        *Dir
	pod         *corev1.Pod
	fuseServer  *fuse.Server
	containerID string
}

// NewK8sFS creates a new K8sFS instance and mounts the filesystem
func NewK8sFS(client *k8s.Client, namespace, podName, container string) (*K8sFS, error) {
	k := &K8sFS{
		client: client,
	}

	// Get pod info
	pod, err := client.GetPod(namespace, podName)
	if err != nil {
		return nil, fmt.Errorf("failed to get pod info: %v", err)
	}
	k.pod = pod

	// Get container ID from pod
	for _, status := range pod.Status.ContainerStatuses {
		if status.Name == container {
			k.containerID = status.ContainerID[strings.LastIndex(status.ContainerID, "://")+3:]
			break
		}
	}

	if k.containerID == "" {
		return nil, fmt.Errorf("container %s not found in pod %s", container, podName)
	}

	return k, nil
}

// Root implements fs.FS interface
func (k *K8sFS) Root() (fs.InodeEmbedder, error) {
	if k.root == nil {
		k.root = &Dir{
			fs:     k,
			parent: nil,
			path:   "/",
		}
	}
	return k.root, nil
}

// Dir implements directory node
type Dir struct {
	fs     *K8sFS
	parent *Dir
	path   string
	fs.Inode
}

// Getattr returns the attributes of the directory
func (d *Dir) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
	info, err := d.fs.client.Stat(d.fs.pod, d.fs.containerID, d.path)
	if err != nil {
		return syscall.ENOENT
	}

	out.Mode = uint32(info.Mode())
	out.Size = uint64(info.Size())
	out.Mtime = uint64(info.ModTime().Unix())
	return 0
}

// Lookup finds a child node by name
func (d *Dir) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (fs.InodeEmbedder, syscall.Errno) {
	path := d.path
	if path != "/" {
		path += "/"
	}
	path += name

	info, err := d.fs.client.Stat(d.fs.pod, d.fs.containerID, path)
	if err != nil {
		return nil, syscall.ENOENT
	}

	if info.IsDir() {
		child := &Dir{
			fs:     d.fs,
			parent: d,
			path:   path,
		}
		return child, 0
	}

	file := &File{
		fs:   d.fs,
		path: path,
	}
	return file, 0
}

// ReadDirAll reads all entries in the directory
func (d *Dir) ReadDirAll(ctx context.Context) (entries []fuse.DirEntry, errno syscall.Errno) {
	entriesList, err := d.fs.client.ReadDir(d.fs.pod, d.fs.containerID, d.path)
	if err != nil {
		return nil, syscall.ENOENT
	}

	for _, entry := range entriesList {
		dirent := fuse.DirEntry{
			Name: entry.Name(),
		}
		if entry.IsDir() {
			dirent.Mode = fuse.S_IFDIR
		} else {
			dirent.Mode = fuse.S_IFREG
		}
		entries = append(entries, dirent)
	}
	return entries, 0
}

// File implements file node
type File struct {
	fs   *K8sFS
	path string
	fs.Inode
}

// Getattr returns the attributes of the file
func (f *File) Getattr(ctx context.Context, fh fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
	info, err := f.fs.client.Stat(f.fs.pod, f.fs.containerID, f.path)
	if err != nil {
		return syscall.ENOENT
	}

	out.Mode = uint32(info.Mode())
	out.Size = uint64(info.Size())
	out.Mtime = uint64(info.ModTime().Unix())
	return 0
}

// ReadAll reads the entire contents of the file
func (f *File) ReadAll(ctx context.Context) ([]byte, syscall.Errno) {
	data, err := f.fs.client.ReadFile(f.fs.pod, f.fs.containerID, f.path)
	if err != nil {
		return nil, syscall.ENOENT
	}
	return data, 0
}

// Mount mounts the filesystem to the specified path using FUSE
func (k *K8sFS) Mount() error {
	root, err := k.Root()
	if err != nil {
		return fmt.Errorf("failed to get root inode: %v", err)
	}
	k.fuseServer, err = fs.Mount(getMountPath(), root, &fs.Options{
		MountOptions: fuse.MountOptions{
			Debug: false,
		},
	})
	if err != nil {
		return fmt.Errorf("failed to mount FUSE filesystem: %v", err)
	}

	go func() {
		k.fuseServer.Wait()
	}()

	return nil
}

func getMountPath() string {
	res := os.Getenv("MOUNT_PATH")
	if res != "" {
		return res
	}
	return mountPath
}

// Unmount unmounts the filesystem
func (k *K8sFS) Unmount() error {
	if err := k.fuseServer.Unmount(); err != nil {
		return fmt.Errorf("failed to unmount FUSE filesystem: %v", err)
	}
	return nil
}

// Update updates the pod for the K8sFS instance
func (k *K8sFS) Update(namespace, podName, container string) error {
	// Get updated pod info
	pod, err := k.client.GetPod(namespace, podName)
	if err != nil {
		return fmt.Errorf("failed to get pod info: %v", err)
	}
	k.pod = pod // 更新 Pod 实例

	// Get container ID from pod
	for _, status := range pod.Status.ContainerStatuses {
		if status.Name == container {
			k.containerID = status.ContainerID[strings.LastIndex(status.ContainerID, "://")+3:]
			break
		}
	}

	if k.containerID == "" {
		return fmt.Errorf("container %s not found in pod %s", container, podName)
	}

	return nil
}
